#pragma once

#include <windows.h>
#include <vector>
#include <memory>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <thread>
#include <chrono>
#include <atomic>

#include "ev_packet.h"

class SerialPort {
public:
    typedef std::shared_ptr<ev_packet_t> MessagePtr;

    SerialPort();
    ~SerialPort();

    SerialPort(const SerialPort&) = delete;
    SerialPort& operator=(const SerialPort&) = delete;

    bool Open(const std::string& portName, int baudRate);
    void Close();
    bool IsOpen() const;

    bool SendData(const uint8_t* data, size_t length);
    bool SendData(const ev_packet_t* pkt);

    bool GetMessage(MessagePtr& message, DWORD timeoutMs = INFINITE);
    bool PeekMessage(MessagePtr& message);
    size_t GetMessageCount() const;

    void SetTimeoutMs(int timeoutMs) { timeoutMs_ = timeoutMs; }
    int GetTimeoutMs() const { return timeoutMs_; }

private:
    void ReadThread();
    void CheckTimeout();

    static void ParserCallback(const ev_parser_t* parser, const ev_packet_t* packet);

    HANDLE hSerial_;
    HANDLE hReadEvent_;
    HANDLE hTimerEvent_;
    std::unique_ptr<std::thread> readThread_;
    std::atomic<bool> bRunning_;

    ev_parser_t parser_;

    typedef std::queue<MessagePtr> MessageQueue;
    mutable std::mutex queueMutex_;
    std::condition_variable queueCondition_;
    MessageQueue messageQueue_;

    std::string portName_;
    int baudRate_;
    int timeoutMs_;

    std::chrono::steady_clock::time_point lastReceiveTime_;
    mutable std::mutex timeMutex_;
};
